package com.jxtc.enterprise.employee.serviceImpl;

import cn.hutool.core.date.DateUtil;
import com.binarywang.spring.starter.wxjava.pay.properties.WxPayProperties;
import com.github.binarywang.wxpay.bean.request.WxPayOrderCloseV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.jxtc.enterprise.common.utils.Result;
import com.jxtc.enterprise.common.utils.ResultCode;
import com.jxtc.enterprise.employee.vo.WxPayVo;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.billdownload.BillDownloadService;
import com.wechat.pay.java.service.billdownload.BillDownloadServiceExtension;
import com.wechat.pay.java.service.billdownload.DigestBillEntity;
import com.wechat.pay.java.service.billdownload.model.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.util.RandomUtils;
import org.apache.commons.io.IOUtils;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;


@Slf4j
@Service
@RequiredArgsConstructor
public class JxWxPayServiceImpl {

    private final WxPayService payService;
    /**
     * application.yml 中 wx.pay 相关配置会自动注入到 wxPayProperties 中
     */
    private final WxPayProperties wxPayProperties;
    /**
     * 微信支付回调链接，两种场景会使用到此回到url
     * 1. 用户在微信端发起支付后，由微信支付平台将支付结果通过回调传递给后端服务
     * 2. 用户在微信端取消订单后，由微信支付平台将退款结果通过回调传递给后端服务
     */
    @Value("${wx.base_notify_url}")
    private String BASE_NOTIFY_URL;

    /**
     * 交易货币类型
     */
    private final String CURRENCY = "CNY";

    public WxPayVo pay(String userId, String openId, String orderId, String description, Integer total, String tag) throws WxPayException {
        log.warn("支付参数 userId: {}, openId: {}, orderId: {}, description: {}, total: {}, tag: {}", userId, openId, orderId, description, total, tag);

        String notifyUrl = BASE_NOTIFY_URL + "notify";

        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
        request.setAppid(wxPayProperties.getAppId()); // 员工小程序appid
        request.setMchid(wxPayProperties.getMchId()); // 商户 merchant id
        request.setDescription(description);
        request.setOutTradeNo(orderId);
        request.setNotifyUrl(notifyUrl);
        request.setAttach(tag);

        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE, 1);
        request.setTimeExpire(DateUtil.format(instance.getTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"));

        //构建金额信息
        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
        //设置币种信息
        amount.setCurrency(CURRENCY);
        //设置金额
        amount.setTotal(total);

        request.setAmount(amount);

        WxPayVo wxPay = new WxPayVo()
                .setUserId(userId)
                .setDescription(description)
                .setOutTradeNo(orderId)
                .setAmountCurrency(CURRENCY)
                .setAmountTotal(total);

        request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(openId));
        WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = this.payService.createOrderV3(TradeTypeEnum.JSAPI, request);
        log.info("订单id: {}, prepay: {}", orderId, jsapiResult);

        wxPay.setAppid(request.getAppid())
                .setMchId(request.getMchid())
                .setTimeStamp(jsapiResult.getTimeStamp())
                .setNonceStr(jsapiResult.getNonceStr())
                .setPackageValue(jsapiResult.getPackageValue())
                .setSignType(jsapiResult.getSignType())
                .setPaySign(jsapiResult.getPaySign());

        return wxPay;
    }

    public WxPayOrderQueryV3Result queryPayResult(String orderId) {
        WxPayOrderQueryV3Result wxPayOrderQueryV3Result = null;
        try {
            wxPayOrderQueryV3Result = this.payService.queryOrderV3(null, orderId);
        } catch (WxPayException e) {
            throw new RuntimeException(e);
        }
        System.out.println(wxPayOrderQueryV3Result);

        return wxPayOrderQueryV3Result;
    }


    public void refund(String outTradeNo, Integer total, Integer refund) throws WxPayException {
        String outRefundNo = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + RandomUtils.getRandomStr();
        String notifyUrl = BASE_NOTIFY_URL + "refundCallback";
        String reason = "取消订单";

        WxPayRefundV3Request request = new WxPayRefundV3Request();
        request.setOutTradeNo(outTradeNo);
        request.setOutRefundNo(outRefundNo);
        request.setReason(reason);
        request.setNotifyUrl(notifyUrl);

        //构建金额信息
        WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
        //设置币种信息
        amount.setCurrency(CURRENCY);
        //设置金额
        amount.setTotal(total);
        amount.setRefund(refund);
        request.setAmount(amount);

        WxPayRefundV3Result wxPayRefundV3Result = this.payService.refundV3(request);
    }

    /**
     * 调用 关闭支付交易 接口，<a href="https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/close-order.html">参考</a>
     *
     * @param outTradeNo 商户订单号
     */
    public Result<?> closeTransaction(String outTradeNo) {

        WxPayOrderCloseV3Request request = new WxPayOrderCloseV3Request();
        request.setOutTradeNo(outTradeNo);
        try {
            // closeOrderV3() 方法无返回值，取消支付 请求是否成功，通过抛出的异常中的 错误码来判断
            // 若不抛出异常，则表示成功
            this.payService.closeOrderV3(request);
            log.info("发起支付 取消成功");
            return new Result<>(ResultCode.SUCCESS.code(), "支付取消成功", "支付取消成功");
        } catch (WxPayException ex) {
            log.info("发起支付 取消失败。订单号：{}，错误信息：{}", outTradeNo, ex.toString());
            return new Result<>(ResultCode.INTERNAL_SERVER_ERROR.code(), "支付取消失败", ex);
        }
    }


    /**
     * 参考链接： <a href="https://github.com/wechatpay-apiv3/wechatpay-java">调用业务请求接口 章节 java示例</a>
     *
     * @param billDate 账单日期，格式 yyyy-MM-DD
     */
    public Result<?> queryTransactionBill(String billDate) {

        try {

            Config config =
                    new RSAAutoCertificateConfig.Builder()
                            .merchantId(wxPayProperties.getMchId()) // "1659232319"
                            .privateKeyFromPath(wxPayProperties.getPrivateKeyPath()) // privateKeyPath
                            .merchantSerialNumber(wxPayProperties.getCertSerialNo()) // "1B5D1FC41283EB90A130458726C49459318E15CC"
                            .apiV3Key(wxPayProperties.getApiv3Key()) // "JixinKeJiCor20231224CorJixinKeJi"
                            .build();
            BillDownloadService billDownloadService = new BillDownloadService.Builder().config(config).build();
            GetTradeBillRequest tradeBillRequest = new GetTradeBillRequest();
            tradeBillRequest.setBillDate(billDate);
            tradeBillRequest.setBillType(BillType.ALL);
            tradeBillRequest.setTarType(TarType.GZIP);

            BillDownloadServiceExtension downloadServiceExtension = new BillDownloadServiceExtension.Builder().config(config).build();
            DigestBillEntity billEntity2 = downloadServiceExtension.getTradeBill(tradeBillRequest);
            InputStream inputStream = billEntity2.getInputStream();
            String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
            log.info("-------------- bill ------------- result: {}", result);

        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 参考链接： <a href="https://github.com/wechatpay-apiv3/wechatpay-java">调用业务请求接口 章节 java示例</a> ，
     *
     * @param billDate 账单日期，格式 yyyy-MM-DD
     */
    public Result<?> queryFundBill(String billDate) {

        try {
            Config config =
                    new RSAAutoCertificateConfig.Builder()
                            .merchantId(wxPayProperties.getMchId()) // "1659232319"
                            .privateKeyFromPath(wxPayProperties.getPrivateKeyPath()) // privateKeyPath
                            .merchantSerialNumber(wxPayProperties.getCertSerialNo()) // "1B5D1FC41283EB90A130458726C49459318E15CC"
                            .apiV3Key(wxPayProperties.getApiv3Key()) // "JixinKeJiCor20231224CorJixinKeJi"
                            .build();
            BillDownloadService billDownloadService = new BillDownloadService.Builder().config(config).build();
            GetFundFlowBillRequest fundBillRequest = new GetFundFlowBillRequest();
            fundBillRequest.setBillDate(billDate);
            fundBillRequest.setAccountType(AccountType.BASIC);  // basic 基本账户， operation 运营账户，fees 手续费账户
            fundBillRequest.setTarType(TarType.GZIP);

            BillDownloadServiceExtension downloadServiceExtension = new BillDownloadServiceExtension.Builder().config(config).build();
            DigestBillEntity billEntity2 = downloadServiceExtension.getFundFlowBill(fundBillRequest);
            InputStream inputStream = billEntity2.getInputStream();
            String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
            log.info("-------------- fund ------------- result: {}", result);

        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}
